// 默认缓存期限为7天
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;

export interface IStorage {
  /**
   * @description 对有前缀的key, 拼接出其真实Key值.
   * @param key
   */
  getKey: (key: string) => string;

  /**
   * @description 按key值读取缓存.
   * @param {string} key 键
   * @param defaultValue 默认值, 当读取不到的时候, 使用默认值.
   */
  get: <T>(key: string, defaultValue: T | null) => T | null;

  /**
   * @description 设置缓存
   * @param {string} key 键
   * @param {*} value 值
   * @param {number | null} expire 过期时间
   */
  set: <T>(key: string, value: T, expire: number | null) => void;

  /**
   * @description 按key值删除某项缓存
   * @param {string} key
   */
  remove: (key: string) => void;

  /**
   *  @description 清空所有缓存
   */
  clear: () => void;

  /**
   * @description 按name读取cookie.
   * @param {string} name cookie的名称
   */
  getCookie: (name: string) => string;

  /**
   * @description 设置缓存
   * @param {string} name cookie的名称
   * @param {string} value cookie的值
   * @param {number | null} expire 过期时间
   */
  setCookie: (name: string, value: string, expire: number | null) => void;

  /**
   * @description 按name删除某项cookie
   * @param {string} name cookie的名称
   */
  removeCookie: (name: string) => void;

  /**
   *  @description 清空所有cookie
   */
  clearCookie: () => void;
}

export const createStorage = (prefixKey = "", storage = localStorage) => {
  class Storage implements IStorage {
    private prefixKey: string = prefixKey;
    private storage = storage;

    /**
     * @description 对有前缀的key, 拼接出其真实Key值.
     * @param key
     */
    getKey(key: string): string {
      return `${this.prefixKey}${key}`.toUpperCase();
    }

    /**
     * @description 按key值读取缓存.
     * @param {string} key 键
     * @param defaultValue 默认值, 当读取不到的时候, 使用默认值.
     */
    get<T>(key: string, defaultValue: T | null = null): T | null {
      const item = this.storage.getItem(this.getKey(key));
      if (item) {
        try {
          const data = JSON.parse(item);
          const { value, expire } = data;
          // 在有效期内直接返回
          if (expire === null || expire >= Date.now()) {
            return value;
          }
          // 超出则删除
          this.remove(key);
        } catch (e) {
          return defaultValue;
        }
      }
      return defaultValue;
    }

    /**
     * @description 设置缓存
     * @param {string} key 键
     * @param {*} value 值
     * @param {number | null} expire 过期时间
     */
    set<T>(
      key: string,
      value: T,
      expire: number | null = DEFAULT_CACHE_TIME
    ): void {
      const expireDate =
        expire !== null ? new Date().getTime() + expire * 1000 : null;
      const stringData = JSON.stringify({ value, expireDate });
      this.storage.setItem(this.getKey(key), stringData);
    }

    /**
     * @description 按key值删除某项缓存
     * @param {string} key
     */
    remove(key: string): void {
      this.storage.removeItem(this.getKey(key));
    }

    /**
     *  @description 清空所有缓存
     */
    clear(): void {
      this.storage.clear();
    }

    /**
     * @description 按name读取cookie.
     * @param {string} name cookie的名称
     */
    getCookie(name: string): string {
      const cookieArr = document.cookie.split("; ");
      const truthKey = this.getKey(name);
      let keyValue = [];
      for (let i = 0; i < cookieArr.length; i++) {
        keyValue = cookieArr[i].split("=");
        if (keyValue[0] === truthKey) {
          return keyValue[1];
        }
      }
      return "";
    }

    /**
     * @description 设置缓存
     * @param {string} name cookie的名称
     * @param {string} value cookie的值
     * @param {number | null} expire 过期时间
     */
    setCookie(
      name: string,
      value: string,
      expire: number | null = DEFAULT_CACHE_TIME
    ): void {
      document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`;
    }

    /**
     * @description 按name删除某项cookie
     * @param {string} name cookie的名称
     */
    removeCookie(name: string): void {
      this.setCookie(name, "1", -1);
    }

    /**
     *  @description 清空所有cookie(即失效所有cookie)
     */
    clearCookie(): void {
      const keys = document.cookie.match(/[^ =;]+(?==)/g);
      if (keys) {
        for (let i = keys.length; i--; ) {
          document.cookie = keys[i] + "=0;expire=" + new Date(0).toUTCString();
        }
      }
    }
  }

  return new Storage();
};

export const storage = createStorage();
